﻿// <copyright file="Server.cs" company="Hallvard Korsgaard">
//  Microsoft Public License (Ms-PL). See license details here: http://enoceannet.codeplex.com/license
// </copyright>
// <author>Hallvard Korsgaard</author>

namespace StcEthernet
{
  using System;
  using System.Collections.Generic;
  using System.Diagnostics;
  using System.Net;
  using System.Net.Sockets;
  using System.Threading;

  /// <summary>
  /// This class creates a server socket and listens for connections from STC-Ethernet clients.
  /// </summary>
  public class Server
  {
    /// <summary>
    /// Socket listening for client connections.
    /// </summary>
    private Socket serverSocket;
 
    /// <summary>
    /// Initializes a new instance of the Server class.
    /// </summary>
    /// <param name="localEndPoint">The network enpoint (IP address) to listen to.</param>
    public Server(IPEndPoint localEndPoint)
    {
      // create the socket which listens for incoming connections
      this.serverSocket = new Socket(localEndPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
      this.serverSocket.Bind(localEndPoint);

      // start the server with a listen backlog of 100 connections
      this.serverSocket.Listen(100);
    }

    /// <summary>
    /// The event fires when a transiever (or receiver) connects.
    /// </summary>
    public event EventHandler<TransieverConnectedEventArgs> TransieverConnected = delegate { };

    /// <summary>
    /// The event fires when all properties has been set by the transiever.
    /// </summary>
    public event EventHandler<TransieverInitializedEventArgs> TransieverInitialized = delegate { };

    /// <summary>
    /// Start to accept connections.
    /// </summary>
    public void Accept()
    {
      SocketAsyncEventArgs acceptArgs;
      bool pending;

      acceptArgs = new SocketAsyncEventArgs();
      acceptArgs.Completed += new EventHandler<SocketAsyncEventArgs>(this.AcceptCompleted);

      pending = this.serverSocket.AcceptAsync(acceptArgs);
      if (!pending)
      {
        this.AcceptCompleted(this, acceptArgs);
      }
    }

    /// <summary>
    /// Closes the server socket. New connections will not be accepted.
    /// </summary>
    public void Close()
    {
      Delegate[] eventHandlers;

      if (this.serverSocket != null)
      {
        this.serverSocket.Close();
        this.serverSocket = null;
      }

      // Remove event sinks to help GC.
      eventHandlers = this.TransieverConnected.GetInvocationList();
      foreach (Delegate d in eventHandlers)
      {
        this.TransieverConnected -= (EventHandler<TransieverConnectedEventArgs>)d;
      }

      eventHandlers = this.TransieverInitialized.GetInvocationList();
      foreach (Delegate d in eventHandlers)
      {
        this.TransieverInitialized -= (EventHandler<TransieverInitializedEventArgs>)d;
      }
    }

    /// <summary>
    /// Event handler for the socket accept event.
    /// </summary>
    /// <param name="sender">The source of the event.</param>
    /// <param name="e">A SocketAsyncEventArgs that contains the event data.</param>
    private void AcceptCompleted(object sender, SocketAsyncEventArgs e)
    {
      Transiever t;

      if (e.SocketError == SocketError.Success)
      {
        t = new Transiever(e.AcceptSocket);
        t.Initialized += new EventHandler<EventArgs>(this.Initialized);
        ThreadPool.QueueUserWorkItem(state => this.TransieverConnected(this, new TransieverConnectedEventArgs((Transiever)state)), t);
        t.Initialize();
       
        this.Accept();
      }
    }

    /// <summary>
    /// Event handler for the Transiever Initialized event.
    /// </summary>
    /// <param name="sender">The source of the event.</param>
    /// <param name="e">A SocketAsyncEventArgs that contains the event data.</param>
    private void Initialized(object sender, EventArgs e)
    {
      ThreadPool.QueueUserWorkItem(state => this.TransieverInitialized(this, new TransieverInitializedEventArgs((Transiever)state)), sender);
    }
  }
}
